<template>
  <div class="three-wrapper">
    <div class="box" ref="box"></div>
    <div class="actions">
      <button @click="startFocus">相机聚焦</button>
      <button @click="resetCamera">重置相机</button>
      <button @click="focusRandomObject">聚焦随机物体</button>
    </div>
  </div>
</template>

<script>
import TWEEN from '@tweenjs/tween.js'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
const clock = new THREE.Clock()
const FPS = 30 // 设置渲染频率为30FBS，也就是每秒调用渲染器render方法大约30次
const renderT = 1 / FPS // 单位秒  间隔多长时间渲染渲染一次
let timeS = 0
export default {
  components: {},
  props: {},
  data() {
    return {
      el: null,
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      animationFrameId: null,
      focusObjects: [], // 存储可聚焦的物体
      originalCameraPos: new THREE.Vector3(300, 300, 300), // 原始相机位置
      originalCameraTarget: new THREE.Vector3(0, 0, 0) // 原始相机目标
    }
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.el = this.$refs['box']
    this.clock = new THREE.Clock()

    this.initThree()
  },
  activated() {},
  deactivated() {},
  updated() {},
  beforeDestroy() {
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId)
    }
  },
  destroyed() {},
  methods: {
    initThree() {
      // 初始化 - 开始
      this.initScene() // 核心： 场景
      this.initCamera() // 核心: 相机
      this.initRender() // 核心 渲染器
      this.initLight()
      this.initControls()
      this.animate()
      this.scene.add(this.setGridHelper(100 * 2, 10 * 2))
      this.scene.add(this.setAxesHelper(100000))
      // 初始化 - 结束

      // 添加物体
      this.addMesh()

      // 保存原始相机位置和目标
      this.originalCameraPos.copy(this.camera.position)
      this.originalCameraTarget.copy(this.controls.target)
    },
    // 相机聚焦到主立方体
    startFocus() {
      console.log('开始相机聚焦')

      // 计算聚焦位置（在主立方体的前方偏上位置）
      const targetObject = this.focusObjects[0] // 主立方体
      if (!targetObject) return

      const focusPosition = this.calculateFocusPosition(targetObject, 30, 45)
      const focusTarget = targetObject.position.clone()

      this.smoothFocus(focusPosition, focusTarget, 1500)
    },
    // 聚焦到随机物体
    focusRandomObject() {
      if (this.focusObjects.length <= 1) return

      // 随机选择一个物体（排除主立方体）
      const randomIndex =
        Math.floor(Math.random() * (this.focusObjects.length - 1)) + 1
      const targetObject = this.focusObjects[randomIndex]

      console.log(`聚焦到随机物体 ${randomIndex}`)

      const focusPosition = this.calculateFocusPosition(targetObject, 25, 30)
      const focusTarget = targetObject.position.clone()

      this.smoothFocus(focusPosition, focusTarget, 1200)
    },
    // 计算聚焦相机位置
    calculateFocusPosition(targetObject, distance, angleDegrees = 45) {
      const targetPos = targetObject.position.clone()
      const boundingBox = new THREE.Box3().setFromObject(targetObject)
      const size = boundingBox.getSize(new THREE.Vector3())
      const objectSize = Math.max(size.x, size.y, size.z)

      // 计算合适的观察距离（基于物体大小）
      const optimalDistance = Math.max(objectSize * 2, distance)

      // 将角度转换为弧度
      const angle = angleDegrees * (Math.PI / 180)

      // 计算相机位置（在物体的斜上方）
      return new THREE.Vector3(
        targetPos.x + optimalDistance * Math.sin(angle),
        targetPos.y + optimalDistance * 0.5, // 稍微上方
        targetPos.z + optimalDistance * Math.cos(angle)
      )
    },
    // 平滑聚焦动画
    smoothFocus(targetPosition, targetLookAt, duration = 1500) {
      // 禁用控制器的阻尼，让动画更流畅
      this.controls.enabled = false

      // 停止所有现有的Tween动画
      TWEEN.removeAll()

      // 相机位置动画
      new TWEEN.Tween(this.camera.position)
        .to(targetPosition, duration)
        .easing(TWEEN.Easing.Quadratic.InOut)
        .onStart(() => {
          console.log('开始相机移动动画')
        })
        .onUpdate(() => {
          // 在移动过程中持续看向目标
          this.camera.lookAt(targetLookAt)
        })
        .onComplete(() => {
          console.log('相机移动完成')
          // 更新控制器目标
          this.controls.target.copy(targetLookAt)
          // 重新启用控制器
          this.controls.enabled = true
        })
        .start()

      // 控制器目标点动画（与相机位置动画同步）
      new TWEEN.Tween(this.controls.target)
        .to(targetLookAt, duration)
        .easing(TWEEN.Easing.Quadratic.InOut)
        .start()
    },
    // 重置相机到初始位置
    resetCamera() {
      console.log('重置相机位置')

      this.controls.enabled = false
      TWEEN.removeAll()

      // 相机
      new TWEEN.Tween(this.camera.position)
        .to(this.originalCameraPos, 1200)
        .easing(TWEEN.Easing.Quadratic.InOut)
        .onUpdate(() => {
          this.camera.lookAt(this.originalCameraTarget)
        })
        .onComplete(() => {
          this.controls.target.copy(this.originalCameraTarget)
          this.controls.enabled = true
        })
        .start()

      // 控制器
      new TWEEN.Tween(this.controls.target)
        .to(this.originalCameraTarget, 1200)
        .easing(TWEEN.Easing.Quadratic.InOut)
        .start()
    },
    addMesh() {
      // 主立方体（绿色）
      const geometry = new THREE.BoxGeometry(10, 10, 10)
      const material = new THREE.MeshLambertMaterial({
        color: new THREE.Color('#00ff00'),
        transparent: true,
        opacity: 0.9
      })
      const mainMesh = new THREE.Mesh(geometry, material)
      mainMesh.position.set(0, 5, 0)
      mainMesh.name = 'main-cube'
      this.scene.add(mainMesh)
      this.focusObjects.push(mainMesh)

      // 添加多个不同颜色和位置的立方体
      this.createAdditionalObjects()
    },
    // 创建额外的物体用于聚焦演示
    createAdditionalObjects() {
      const colors = [
        0xff6b6b, // 红色
        0x4ecdc4, // 青色
        0x45b7d1, // 蓝色
        0x96ceb4, // 绿色
        0xffeaa7, // 黄色
        0xdda0dd, // 紫色
        0xffa07a // 橙色
      ]

      const shapes = [
        { type: 'box', size: [8, 12, 8] },
        { type: 'sphere', size: [6] },
        { type: 'cylinder', size: [5, 10, 5] },
        { type: 'cone', size: [7, 10, 7] }
      ]

      for (let i = 0; i < 12; i++) {
        let geometry
        const shapeType = shapes[i % shapes.length]

        switch (shapeType.type) {
          case 'box':
            geometry = new THREE.BoxGeometry(...shapeType.size)
            break
          case 'sphere':
            geometry = new THREE.SphereGeometry(...shapeType.size)
            break
          case 'cylinder':
            geometry = new THREE.CylinderGeometry(...shapeType.size)
            break
          case 'cone':
            geometry = new THREE.ConeGeometry(...shapeType.size)
            break
        }

        const material = new THREE.MeshLambertMaterial({
          color: colors[i % colors.length],
          transparent: true,
          opacity: 0.8
        })

        const mesh = new THREE.Mesh(geometry, material)

        // 分布在场景中的不同位置
        const angle = (i / 12) * Math.PI * 2
        const radius = 60 + Math.random() * 40
        mesh.position.set(
          Math.cos(angle) * radius,
          shapeType.size[1] / 2 || 5,
          Math.sin(angle) * radius
        )

        // 随机旋转
        mesh.rotation.set(
          Math.random() * Math.PI,
          Math.random() * Math.PI,
          Math.random() * Math.PI
        )

        mesh.name = `object-${i}`
        this.scene.add(mesh)
        this.focusObjects.push(mesh)
      }
    },
    setGridHelper(size, divisions) {
      const gridHelper = new THREE.GridHelper(size, divisions)
      gridHelper.material.opacity = 0.2
      gridHelper.material.transparent = true
      return gridHelper
    },
    setAxesHelper(size) {
      return new THREE.AxesHelper(size)
    },
    // 初始化场景
    initScene() {
      this.scene = new THREE.Scene()
    },
    // 初始化相机
    initCamera() {
      const asp = this.el.offsetWidth / this.el.offsetHeight
      this.camera = new THREE.PerspectiveCamera(45, asp, 0.1, 10000000)
      this.camera.position.set(300, 300, 300)
      this.camera.lookAt(this.scene.position)
      this.scene.add(this.camera)
    },
    initRender(clearColor = '#000') {
      const width = this.el.offsetWidth
      const height = this.el.offsetHeight
      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      })
      this.renderer.setPixelRatio(window.devicePixelRatio)
      this.renderer.setSize(width, height)
      this.el.append(this.renderer.domElement)
    },
    initLight() {
      const ambientLight = new THREE.AmbientLight('#fff', 0.4)
      this.scene.add(ambientLight)
      const directionalLight = new THREE.DirectionalLight('#fff', 0.8)
      directionalLight.position.set(30, 30, 30).normalize()
      this.scene.add(directionalLight)
    },
    initControls() {
      this.controls = new OrbitControls(
        this.camera,
        this.renderer.domElement,
        {}
      )
      this.controls.enableDamping = true
      this.controls.dampingFactor = 0.05
    },
    render() {
      const dt = this.clock.getDelta()
      const T = clock.getDelta()
      timeS = timeS + T
      if (timeS > renderT) {
        TWEEN.update() // note: 这个代码不能少 否则动画不会动

        this.controls.update(dt)
        this.renderer.render(this.scene, this.camera)
      }
    },
    animate() {
      this.render()
      this.animationFrameId = requestAnimationFrame(this.animate)
    }
  },
  filters: {}
}
</script>
<style scoped lang="scss">
.three-wrapper {
  height: 100%;
  position: relative;
  .box {
    height: 100%;
    width: 100%;
    background: url(./images/map-bg.png);
    background-size: cover;
    background-repeat: no-repeat;
    overflow: hidden;
  }
  .actions {
    position: absolute;
    bottom: 10px;
    left: 10px;
    display: flex;
    gap: 10px;

    button {
      padding: 8px 16px;
      border: none;
      background-color: #409eff;
      color: #fff;
      border-radius: 6px;
      cursor: pointer;
      font-size: 14px;
      transition: all 0.3s ease;

      &:hover {
        background-color: #66b1ff;
        transform: translateY(-2px);
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
      }

      &:nth-child(2) {
        background-color: #e6a23c;
        &:hover {
          background-color: #ebb563;
        }
      }

      &:nth-child(3) {
        background-color: #67c23a;
        &:hover {
          background-color: #85ce61;
        }
      }
    }
  }
}
</style>
